pit_migrate_timers(v);
rtc_migrate_timers(v);
pmtimer_migrate_timers(v);
- migrate_timer(&vcpu_vlapic(v)->vlapic_timer, v->processor);
+ if ( vcpu_vlapic(v)->pt.enabled )
+ migrate_timer(&vcpu_vlapic(v)->pt.timer, v->processor);
}
void hvm_do_resume(struct vcpu *v)
}
}
-void hvm_interrupt_post(struct vcpu *v, int vector, int type)
-{
- pt_intr_post(v, vector, type);
-
- switch(type) {
- case APIC_DM_EXTINT:
- break;
-
- default:
- vlapic_post_injection(v, vector, type);
- break;
- }
-}
-
-
void hvm_io_assist(struct vcpu *v)
{
vcpu_iodata_t *vio;
int cpu_has_pending_irq(struct vcpu *v)
{
struct hvm_domain *plat = &v->domain->arch.hvm_domain;
- int dummy;
/* APIC */
- if ( cpu_get_apic_interrupt(v, &dummy) != -1 )
+ if ( vlapic_has_interrupt(v) != -1 )
return 1;
/* PIC */
int is_irq_masked(struct vcpu *v, int irq)
{
+ if ( is_lvtt(v, irq) )
+ return !is_lvtt_enabled(v);
+
if ( v->domain->arch.hvm_domain.irq.vpic[irq >> 3].imr & (1 << (irq & 7))
&& domain_vioapic(v->domain)->redirtbl[irq].fields.mask )
return 1;
break;
}
- hvm_interrupt_post(v, intr_vector, intr_type);
+ pt_intr_post(v, intr_vector, intr_type);
}
/*
#define VLAPIC_VERSION 0x00050014
#define VLAPIC_LVT_NUM 6
-extern u32 get_apic_bus_cycle(void);
-
-#define APIC_BUS_CYCLE_NS (((s_time_t)get_apic_bus_cycle()) / 1000)
+/* vlapic's frequence is 100 MHz */
+#define APIC_BUS_CYCLE_NS 10
#define LVT_MASK \
APIC_LVT_MASKED | APIC_SEND_PENDING | APIC_VECTOR_MASK
return vlapic_test_and_set_vector(vector, vlapic->regs + APIC_IRR);
}
-static void vlapic_set_irr(int vector, struct vlapic *vlapic)
-{
- vlapic_set_vector(vector, vlapic->regs + APIC_IRR);
-}
-
static void vlapic_clear_irr(int vector, struct vlapic *vlapic)
{
vlapic_clear_vector(vector, vlapic->regs + APIC_IRR);
static uint32_t vlapic_get_tmcct(struct vlapic *vlapic)
{
- uint32_t counter_passed;
- s_time_t passed, now = NOW();
- uint32_t tmcct = vlapic_get_reg(vlapic, APIC_TMCCT);
-
- if ( unlikely(now <= vlapic->timer_last_update) )
- {
- passed = ~0x0LL - vlapic->timer_last_update + now;
- HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "time elapsed.");
- }
- else
- passed = now - vlapic->timer_last_update;
+ struct vcpu *v = current;
+ uint32_t tmcct, tmict = vlapic_get_reg(vlapic, APIC_TMICT);
+ uint64_t counter_passed;
- counter_passed = passed / (APIC_BUS_CYCLE_NS * vlapic->timer_divisor);
-
- tmcct -= counter_passed;
-
- if ( tmcct <= 0 )
- {
- if ( unlikely(!vlapic_lvtt_period(vlapic)) )
- {
- tmcct = 0;
- /* FIXME: should we add interrupt here? */
- }
- else
- {
- do {
- tmcct += vlapic_get_reg(vlapic, APIC_TMICT);
- } while ( tmcct <= 0 );
- }
- }
-
- vlapic->timer_last_update = now;
- vlapic_set_reg(vlapic, APIC_TMCCT, tmcct);
+ counter_passed = (hvm_get_guest_time(v) - vlapic->pt.last_plt_gtime) // TSC
+ * 1000000000ULL / ticks_per_sec(v) // NS
+ / APIC_BUS_CYCLE_NS / vlapic->timer_divisor;
+ tmcct = tmict - counter_passed;
HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER,
- "timer initial count 0x%x, timer current count 0x%x, "
- "update 0x%016"PRIx64", now 0x%016"PRIx64", offset 0x%x.",
- vlapic_get_reg(vlapic, APIC_TMICT),
- vlapic_get_reg(vlapic, APIC_TMCCT),
- vlapic->timer_last_update, now, counter_passed);
+ "timer initial count %d, timer current count %d, "
+ "offset %"PRId64".",
+ tmict, tmcct, counter_passed);
return tmcct;
}
/* Update the demangled timer_divisor. */
val = ((val & 3) | ((val & 8) >> 1)) + 1;
vlapic->timer_divisor = 1 << (val & 7);
+
+ HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER,
+ "vlapic_set_tdcr timer_divisor: %d.", vlapic->timer_divisor);
}
static void vlapic_read_aligned(struct vlapic *vlapic, unsigned int offset,
* According to the IA32 Manual, all accesses should be 32 bits.
* Some OSes do 8- or 16-byte accesses, however.
*/
+ val &= 0xffffffff;
if ( len != 4 )
{
unsigned int tmp;
break;
case APIC_LVTT: /* LVT Timer Reg */
+ vlapic->pt.irq = val & APIC_VECTOR_MASK;
case APIC_LVTTHMR: /* LVT Thermal Monitor */
case APIC_LVTPC: /* LVT Performance Counter */
case APIC_LVT0: /* LVT LINT0 Reg */
case APIC_TMICT:
{
- s_time_t now = NOW(), offset;
-
- stop_timer(&vlapic->vlapic_timer);
+ uint64_t period = APIC_BUS_CYCLE_NS * (uint32_t)val * vlapic->timer_divisor;
vlapic_set_reg(vlapic, APIC_TMICT, val);
- vlapic_set_reg(vlapic, APIC_TMCCT, val);
- vlapic->timer_last_update = now;
-
- offset = APIC_BUS_CYCLE_NS * vlapic->timer_divisor * val;
-
- set_timer(&vlapic->vlapic_timer, now + offset);
+ create_periodic_time(&vlapic->pt, period, vlapic->pt.irq,
+ vlapic_lvtt_period(vlapic), NULL, vlapic);
HVM_DBG_LOG(DBG_LEVEL_VLAPIC,
- "bus cycle is %"PRId64"ns, now 0x%016"PRIx64", "
- "timer initial count 0x%x, offset 0x%016"PRIx64", "
- "expire @ 0x%016"PRIx64".",
- APIC_BUS_CYCLE_NS, now,
- vlapic_get_reg(vlapic, APIC_TMICT),
- offset, now + offset);
+ "bus cycle is %uns, "
+ "initial count %lu, period %"PRIu64"ns",
+ APIC_BUS_CYCLE_NS, val, period);
}
break;
"apic base msr is 0x%016"PRIx64".", vlapic->apic_base_msr);
}
-void vlapic_timer_fn(void *data)
-{
- struct vlapic *vlapic = data;
- uint32_t timer_vector;
- s_time_t now;
-
- if ( unlikely(!vlapic_enabled(vlapic) ||
- !vlapic_lvt_enabled(vlapic, APIC_LVTT)) )
- return;
-
- timer_vector = vlapic_lvt_vector(vlapic, APIC_LVTT);
- now = NOW();
-
- vlapic->timer_last_update = now;
-
- if ( vlapic_test_and_set_irr(timer_vector, vlapic) )
- vlapic->timer_pending_count++;
-
- if ( vlapic_lvtt_period(vlapic) )
- {
- s_time_t offset;
- uint32_t tmict = vlapic_get_reg(vlapic, APIC_TMICT);
-
- vlapic_set_reg(vlapic, APIC_TMCCT, tmict);
-
- offset = APIC_BUS_CYCLE_NS * vlapic->timer_divisor * tmict;
-
- set_timer(&vlapic->vlapic_timer, now + offset);
- }
- else
- vlapic_set_reg(vlapic, APIC_TMCCT, 0);
-
- vcpu_kick(vlapic_vcpu(vlapic));
-
- HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER,
- "now 0x%016"PRIx64", expire @ 0x%016"PRIx64", "
- "timer initial count 0x%x, timer current count 0x%x.",
- now, vlapic->vlapic_timer.expires,
- vlapic_get_reg(vlapic, APIC_TMICT),
- vlapic_get_reg(vlapic, APIC_TMCCT));
-}
-
int vlapic_accept_pic_intr(struct vcpu *v)
{
struct vlapic *vlapic = vcpu_vlapic(v);
vlapic_hw_disabled(vlapic)));
}
-int cpu_get_apic_interrupt(struct vcpu *v, int *mode)
+int vlapic_has_interrupt(struct vcpu *v)
{
struct vlapic *vlapic = vcpu_vlapic(v);
int highest_irr;
((highest_irr & 0xF0) <= vlapic_get_ppr(vlapic)) )
return -1;
- *mode = APIC_DM_FIXED;
return highest_irr;
}
-void vlapic_post_injection(struct vcpu *v, int vector, int deliver_mode)
+int cpu_get_apic_interrupt(struct vcpu *v, int *mode)
{
+ int vector = vlapic_has_interrupt(v);
struct vlapic *vlapic = vcpu_vlapic(v);
- switch ( deliver_mode )
- {
- case APIC_DM_FIXED:
- case APIC_DM_LOWEST:
- vlapic_set_vector(vector, vlapic->regs + APIC_ISR);
- vlapic_clear_irr(vector, vlapic);
- if ( (vector == vlapic_lvt_vector(vlapic, APIC_LVTT)) &&
- (vlapic->timer_pending_count != 0) )
- {
- vlapic->timer_pending_count--;
- vlapic_set_irr(vector, vlapic);
- }
- break;
-
- case APIC_DM_REMRD:
- gdprintk(XENLOG_WARNING, "Ignoring delivery mode 3.\n");
- break;
-
- case APIC_DM_SMI:
- case APIC_DM_NMI:
- case APIC_DM_INIT:
- case APIC_DM_STARTUP:
- break;
+ if ( vector == -1 )
+ return -1;
+
+ vlapic_set_vector(vector, vlapic->regs + APIC_ISR);
+ vlapic_clear_irr(vector, vlapic);
- default:
- gdprintk(XENLOG_WARNING, "Invalid delivery mode\n");
- break;
- }
+ *mode = APIC_DM_FIXED;
+ return vector;
}
/* Reset the VLPAIC back to its power-on/reset state. */
if ( v->vcpu_id == 0 )
vlapic->apic_base_msr |= MSR_IA32_APICBASE_BSP;
- init_timer(&vlapic->vlapic_timer,
- vlapic_timer_fn, vlapic, v->processor);
+ init_timer(&vlapic->pt.timer, pt_timer_fn, &vlapic->pt, v->processor);
return 0;
}
{
struct vlapic *vlapic = vcpu_vlapic(v);
- kill_timer(&vlapic->vlapic_timer);
+ kill_timer(&vlapic->pt.timer);
unmap_domain_page_global(vlapic->regs);
free_domheap_page(vlapic->regs_page);
}
+
+int is_lvtt(struct vcpu *v, int vector)
+{
+ return vcpu_vlapic(v)->pt.enabled &&
+ vector == vlapic_lvt_vector(vcpu_vlapic(v), APIC_LVTT);
+}
+
+int is_lvtt_enabled(struct vcpu *v)
+{
+ if ( unlikely(!vlapic_enabled(vcpu_vlapic(v))) ||
+ !vlapic_lvt_enabled(vcpu_vlapic(v), APIC_LVTT))
+ return 0;
+
+ return 1;
+}
BUG();
break;
}
-
- hvm_interrupt_post(v, highest_vector, intr_type);
+
+ pt_intr_post(v, highest_vector, intr_type);
}
/*
--- /dev/null
+/*
+ * vpt.c: Virtual Platform Timer
+ *
+ * Copyright (c) 2006, Xiaowei Yang, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+#include <xen/time.h>
+#include <asm/hvm/support.h>
+#include <asm/hvm/vpt.h>
+#include <asm/event.h>
+
+static __inline__ void missed_ticks(struct periodic_time *pt)
+{
+ s_time_t missed_ticks;
+
+ missed_ticks = NOW() - pt->scheduled;
+ if ( missed_ticks > 0 )
+ {
+ missed_ticks = missed_ticks / (s_time_t) pt->period + 1;
+ if ( missed_ticks > 1000 )
+ {
+ /* TODO: Adjust guest time together */
+ pt->pending_intr_nr++;
+ }
+ else
+ {
+ pt->pending_intr_nr += missed_ticks;
+ }
+ pt->scheduled += missed_ticks * pt->period;
+ }
+}
+
+void pt_freeze_time(struct vcpu *v)
+{
+ struct list_head *head = &v->arch.hvm_vcpu.tm_list;
+ struct list_head *list;
+ struct periodic_time *pt;
+
+ if ( test_bit(_VCPUF_blocked, &v->vcpu_flags) )
+ return;
+
+ v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
+
+ list_for_each( list, head )
+ {
+ pt = list_entry(list, struct periodic_time, list);
+ stop_timer(&pt->timer);
+ }
+}
+
+void pt_thaw_time(struct vcpu *v)
+{
+ struct list_head *head = &v->arch.hvm_vcpu.tm_list;
+ struct list_head *list;
+ struct periodic_time *pt;
+
+ if ( v->arch.hvm_vcpu.guest_time )
+ {
+ hvm_set_guest_time(v, v->arch.hvm_vcpu.guest_time);
+ v->arch.hvm_vcpu.guest_time = 0;
+
+ list_for_each( list, head )
+ {
+ pt = list_entry(list, struct periodic_time, list);
+ missed_ticks(pt);
+ set_timer(&pt->timer, pt->scheduled);
+ }
+ }
+}
+
+/* Hook function for the platform periodic time */
+void pt_timer_fn(void *data)
+{
+ struct periodic_time *pt = data;
+
+ pt->pending_intr_nr++;
+ pt->scheduled += pt->period;
+
+ missed_ticks(pt);
+
+ if ( !pt->one_shot )
+ set_timer(&pt->timer, pt->scheduled);
+
+ vcpu_kick(pt->vcpu);
+}
+
+void pt_update_irq(struct vcpu *v)
+{
+ struct list_head *head = &v->arch.hvm_vcpu.tm_list;
+ struct list_head *list;
+ struct periodic_time *pt;
+ uint64_t max_lag = -1ULL;
+ int irq = -1;
+
+ list_for_each( list, head )
+ {
+ pt = list_entry(list, struct periodic_time, list);
+ if ( !is_irq_masked(v, pt->irq) && pt->pending_intr_nr
+ && pt->last_plt_gtime + pt->period < max_lag )
+ {
+ max_lag = pt->last_plt_gtime + pt->period;
+ irq = pt->irq;
+ }
+ }
+
+ if ( is_lvtt(v, irq) )
+ vlapic_set_irq(vcpu_vlapic(v), irq, 0);
+ else if ( irq >= 0 )
+ {
+ hvm_isa_irq_deassert(v->domain, irq);
+ hvm_isa_irq_assert(v->domain, irq);
+ }
+}
+
+struct periodic_time *is_pt_irq(struct vcpu *v, int vector, int type)
+{
+ struct list_head *head = &v->arch.hvm_vcpu.tm_list;
+ struct list_head *list;
+ struct periodic_time *pt;
+ struct RTCState *rtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
+ int vec;
+
+ list_for_each( list, head )
+ {
+ pt = list_entry(list, struct periodic_time, list);
+ if ( !pt->pending_intr_nr )
+ continue;
+
+ if ( is_lvtt(v, pt->irq) )
+ {
+ if (pt->irq == vector)
+ return pt;
+ else
+ continue;
+ }
+
+ vec = get_intr_vector(v, pt->irq, type);
+
+ /* RTC irq need special care */
+ if ( vector != vec || (pt->irq == 8 && !is_rtc_periodic_irq(rtc)) )
+ continue;
+
+ return pt;
+ }
+
+ return NULL;
+}
+
+void pt_intr_post(struct vcpu *v, int vector, int type)
+{
+ struct periodic_time *pt = is_pt_irq(v, vector, type);
+
+ if (pt == NULL)
+ return;
+
+ pt->pending_intr_nr--;
+ pt->last_plt_gtime += pt->period_cycles;
+ hvm_set_guest_time(pt->vcpu, pt->last_plt_gtime);
+
+ if (pt->cb)
+ pt->cb(pt->vcpu, pt->priv);
+}
+
+/* If pt is enabled, discard pending intr */
+void pt_reset(struct vcpu *v)
+{
+ struct list_head *head = &v->arch.hvm_vcpu.tm_list;
+ struct list_head *list;
+ struct periodic_time *pt;
+
+ list_for_each( list, head )
+ {
+ pt = list_entry(list, struct periodic_time, list);
+ if ( pt->enabled )
+ {
+ pt->pending_intr_nr = 0;
+ pt->last_plt_gtime = hvm_get_guest_time(pt->vcpu);
+ pt->scheduled = NOW() + pt->period;
+ set_timer(&pt->timer, pt->scheduled);
+ }
+ }
+}
+
+void create_periodic_time(struct periodic_time *pt, uint64_t period,
+ uint8_t irq, char one_shot, time_cb *cb, void *data)
+{
+ destroy_periodic_time(pt);
+
+ pt->enabled = 1;
+ if (period < 900000) /* < 0.9 ms */
+ {
+ printk("HVM_PlatformTime: program too small period %"PRIu64"\n", period);
+ period = 900000; /* force to 0.9ms */
+ }
+ pt->period = period;
+ pt->vcpu = current;
+ pt->last_plt_gtime = hvm_get_guest_time(pt->vcpu);
+ pt->irq = irq;
+ pt->period_cycles = (u64)period * cpu_khz / 1000000L;
+ pt->one_shot = one_shot;
+ pt->scheduled = NOW() + period;
+ pt->cb = cb;
+ pt->priv = data;
+
+ list_add(&pt->list, ¤t->arch.hvm_vcpu.tm_list);
+ set_timer(&pt->timer, pt->scheduled);
+}
+
+void destroy_periodic_time(struct periodic_time *pt)
+{
+ if ( pt->enabled )
+ {
+ pt->enabled = 0;
+ pt->pending_intr_nr = 0;
+ list_del(&pt->list);
+ stop_timer(&pt->timer);
+ }
+}
#include <asm/msr.h>
#include <public/hvm/ioreq.h>
+#include <asm/hvm/vpt.h>
#define MAX_VECTOR 256
#define vlapic_enabled(vlapic) (!vlapic_disabled(vlapic))
struct vlapic {
- uint64_t apic_base_msr;
- uint32_t disabled; /* VLAPIC_xx_DISABLED */
- uint32_t timer_divisor;
- struct timer vlapic_timer;
- int timer_pending_count;
- s_time_t timer_last_update;
- struct page_info *regs_page;
- void *regs;
+ uint64_t apic_base_msr;
+ uint32_t disabled; /* VLAPIC_xx_DISABLED */
+ uint32_t timer_divisor;
+ struct periodic_time pt;
+ int timer_pending_count;
+ s_time_t timer_last_update;
+ struct page_info *regs_page;
+ void *regs;
};
static inline uint32_t vlapic_get_reg(struct vlapic *vlapic, uint32_t reg)
*((uint32_t *)(vlapic->regs + reg)) = val;
}
-
int vlapic_set_irq(struct vlapic *vlapic, uint8_t vec, uint8_t trig);
-void vlapic_post_injection(struct vcpu *v, int vector, int deliver_mode);
-
int vlapic_find_highest_irr(struct vlapic *vlapic);
+int vlapic_has_interrupt(struct vcpu *v);
int cpu_get_apic_interrupt(struct vcpu *v, int *mode);
int vlapic_init(struct vcpu *v);
int vlapic_match_logical_addr(struct vlapic *vlapic, uint8_t mda);
+int is_lvtt(struct vcpu *v, int vector);
+int is_lvtt_enabled(struct vcpu *v);
+
#endif /* __ASM_X86_HVM_VLAPIC_H__ */
struct list_head list;
char enabled;
char one_shot; /* one shot time */
- int irq;
+ u8 irq;
struct vcpu *vcpu; /* vcpu timer interrupt delivers to */
u32 pending_intr_nr; /* the couner for pending timer interrupts */
- u32 period; /* frequency in ns */
+ u64 period; /* frequency in ns */
u64 period_cycles; /* frequency in cpu cycles */
s_time_t scheduled; /* scheduled timer interrupt */
u64 last_plt_gtime; /* platform time when last IRQ is injected */
struct periodic_time *is_pt_irq(struct vcpu *v, int vector, int type);
void pt_intr_post(struct vcpu *v, int vector, int type);
void pt_reset(struct vcpu *v);
-void create_periodic_time(struct periodic_time *pt, u32 period, char irq,
- char one_shot, time_cb *cb, void *data);
+void create_periodic_time(struct periodic_time *pt, uint64_t period,
+ uint8_t irq, char one_shot, time_cb *cb, void *data);
void destroy_periodic_time(struct periodic_time *pt);
int pv_pit_handler(int port, int data, int write);